//////////////////////////////////////////////////////////////////////////

#ifndef _FIELD_H_
#include "field.h"
#endif

//////////////////////////////////////////////////////////////////////////

#ifndef _CLIENTMAPPARSER_H_
#include "clientmapparser.h"
#endif
       
#ifndef _TIMER_H_
#include "timer.h"
#endif
       
#ifndef _IMAGESURFACE_H_
#include "imagesurface.h"
#endif

#ifndef _IMAGES_H_
#include "images.h"
#endif

#ifndef _SOUNDS_H_
#include "sounds.h"
#endif

#ifndef _THEME_H_
#include "theme.h"
#endif

#ifndef _SCOPEGUARD_H_
#include "scopeguard.h"
#endif

#ifndef _SERVERSTATEPACKET_H_
#include "serverstatepacket.h"
#endif

#include <SDL\SDL.h>
#include <SDL_mixer.h>

//////////////////////////////////////////////////////////////////////////


GUI_Field::GUI_Field():
    m_FirstState( PlayersList(), BulletsList(), WallsList() ),
    m_SecondState( PlayersList(), BulletsList(), WallsList() ),
    m_FirstToSecondCoeff(0.0)
{
    m_Background = new GUI_ImageSurface(GUI_Images::MapBackground);
    for (int i = 0; i < 6; ++i)
        m_Players[i] = new GUI_ImageSurface((GUI_Images::Image)(GUI_Images::Player1 + i));

    m_Highlight = new GUI_ImageSurface(GUI_Images::PlayerHighlight);
    m_HUDBack = new GUI_ImageSurface(GUI_Images::HUDBack);
    m_Bullet = new GUI_ImageSurface(GUI_Images::GameBullet);
    m_Wall = new GUI_ImageSurface(GUI_Images::GameWall);
    m_Heart = new GUI_ImageSurface(GUI_Images::HUDHeart);
    m_BackSurface = 0;
//    m_ShootSound = Mix_LoadWAV(".\\rc\\shoot.wav");
}


//////////////////////////////////////////////////////////////////////////


GUI_Field::~GUI_Field()
{
//    Mix_FreeChunk(m_ShootSound);
    delete m_Background;
    for (int i = 0; i < 6; ++i)
        delete m_Players[i];
    delete m_HUDBack;
    delete m_Highlight;
    delete m_Bullet;
    delete m_Wall;
    delete m_Heart;
}


//////////////////////////////////////////////////////////////////////////


void GUI_Field::Resize()
{
    m_Background->Resize();
    for (int i = 0; i < 6; ++i)
        m_Players[i]->Resize();
    m_Highlight->Resize();
    m_HUDBack->Resize();
    m_Bullet->Resize();
    m_Wall->Resize();
    m_Heart->Resize();
}


//////////////////////////////////////////////////////////////////////////


bool GUI_Field::Render(SDL_Surface *_mainSurface, bool _repaint)                                    
{
    // Lock render queue to prevent adding new states from server for a while
    m_RenderQueue.Lock();

    // Fetch states
    bool canIntegrate = FetchTwoStatesAndCoeff();

    // Unlock queue before rendering, because we have already copied info
    // to first and second state and we do not need queue any more.
    // And because rendering can take a lot of time.
    m_RenderQueue.Unlock();


    // If two states taken successfully
    if (canIntegrate)
    {
        // Integrate and put intermediate state into battlefield
        m_Battlefield.Integrate(m_FirstState, m_SecondState, m_FirstToSecondCoeff);

    }
    // Render intermediate state

    RenderBattlefield(_mainSurface);
    return true;
}


////////////////////////////////////////////////////////////////////////////////


bool GUI_Field::FetchTwoStatesAndCoeff()   
{
    // This function works correctly only if difference between 2 renderings
    // is less than difference between 2 states, e.g. each state is rendered
    // several times.


    // Number of packets with state to render
    size_t queueSize = m_RenderQueue.size();

    if ( queueSize < 2 )
        // Less than 2 packets in queue - it's not enough to render
        return false;

    // Render speed multiplier - IT IS VERY IMPORTANT parameter - do not remove it!
    // Without speed multiplier client buffers more and more packets,
    // drawing them slowly (seems like with a big delay)
    // Normal render speed is when there is 5 packets in queue
    double speedMultiplier = queueSize * 0.5; // now it is 2 packets


    // Previous packet render time
    static size_t prevPacketTime = Timer::GetCurrTime();

    // Current render time
    size_t currTime = Timer::GetCurrTime();

    // Delta time from previous render
    size_t timeFromPrevRender = static_cast<size_t>(
        static_cast<double>(currTime - prevPacketTime) * speedMultiplier );

    // Time of first and second state
    size_t firstServerTime = m_RenderQueue[0].first;
    size_t secondServerTime = m_RenderQueue[1].first;

    // Current server time
    size_t currServerTime = firstServerTime + timeFromPrevRender;

    if (currServerTime <= secondServerTime)
    {
        // We are still between these two states. 
        // Save them.
        m_FirstState = m_RenderQueue[0].second;
        m_SecondState = m_RenderQueue[1].second;

        // Calculate first-to-second coefficient
        m_FirstToSecondCoeff = static_cast<double>(secondServerTime - currServerTime) /
                               static_cast<double>(secondServerTime - firstServerTime);

        // States successfully fetched!
        return true;
    }
    else
    {
        // We are not between these states.
        // Get next 2 states and process them.
        m_RenderQueue.pop_front();

        // Update prev time
        prevPacketTime = currTime;

        return FetchTwoStatesAndCoeff();
    }
}


//////////////////////////////////////////////////////////////////////////


void GUI_Field::RedrawBackground()
{
    m_Background->Blt(m_BackSurface, 0.5, 0.5);
    for ( size_t i = 0; i < m_Points.size(); ++i )
        m_Wall->Blt(m_BackSurface, m_Points[i].x / GUI_Theme::GetResizeFactor(), m_Points[i].y / GUI_Theme::GetResizeFactor());
}


////////////////////////////////////////////////////////////////////////////////


void GUI_Field::DrawBackground(const Points& _points)
{
    m_Points = _points;
    if (m_BackSurface)
        delete m_BackSurface;
    m_BackSurface = SDL_CreateRGBSurface(SDL_HWSURFACE,GUI_Theme::GetResizeFactor(),GUI_Theme::GetResizeFactor() / 1.6, 32, 0, 0, 0, 0);
}


void GUI_Field::RenderBattlefield(SDL_Surface *_mainSurface)       
{

    // Draw background
    //m_Graphics.DrawObject(m_Background, 0, 0);
    //SDL_Rect rect;
    //rect.x = (0.5) * GUI_Theme::GetResizeFactor() - m_BackSurface->w / 2;
    //rect.y = (0.5) * GUI_Theme::GetResizeFactor() / 1.6 - m_BackSurface->h / 2;
    //
    //SDL_BlitSurface(m_BackSurface, NULL, _mainSurface, &rect);


    m_Background->Blt(_mainSurface, 0.5, 0.5);
    for ( size_t i = 0; i < m_Points.size(); ++i )
        m_Wall->Blt(_mainSurface, 
            m_Points[i].x / 1280.0, 
            m_Points[i].y / 1280.0 * 1.6);

    // Draw FPS
//    DrawFPS();

    // Get players
    const PlayersList& players = m_Battlefield.GetPlayers();
    for (PlayersList::const_iterator it = players.begin();
        it != players.end(); ++it)
    {
        const Player& player = *it;
        float xPos = (float)(player.GetPosition().x) / 1280.0;
        float yPos = (float)(player.GetPosition().y) / 1280.0 * 1.6;

        if (m_ID == player.GetID())
            m_Highlight->Blt(_mainSurface, xPos, yPos);
        m_Players[player.GetID() % 6]->Blt(_mainSurface, xPos, yPos);
    }

    // Get bullets
    const BulletsList& bullets = m_Battlefield.GetBullets();
    for (BulletsList::const_iterator it = bullets.begin();
        it != bullets.end(); ++it)
    {
        const Bullet& bullet = *it;
        float xPos = (float)(bullet.GetPosition().x) / 1280.0;
        float yPos = (float)(bullet.GetPosition().y) / 1280.0 * 1.6;

        m_Bullet->Blt(_mainSurface, xPos, yPos);
    }

    // Get walls
    const WallsList& walls = m_Battlefield.GetWalls();
    for (WallsList::const_iterator it = walls.begin();
        it != walls.end(); ++it)
    {
        const Wall& wall = *it;
        float xPos = (float)(wall.GetPosition().x) / 1280.0;
        float yPos = (float)(wall.GetPosition().y) / 1280.0 * 1.6;

        m_Wall->Blt(_mainSurface, xPos, yPos);
    }

    // Draw health points
    m_HUDBack->Blt(_mainSurface, 0, 0, GUI_Surface::AllignLeft | GUI_Surface::AllignTop, 0, 0);
    m_Highlight->Blt(_mainSurface, 0.037, 0.037);
    m_Players[m_ID % 6]->Blt(_mainSurface, 0.037, 0.037);
    int health = m_Battlefield.GetHealthPoints(m_ID)/20;
    for ( int i = 0; i < health; i++ )
        m_Heart->Blt(_mainSurface, 0.075+0.020*i, 0.037);

    if  (m_Battlefield.BulletsAdded())
        Mix_PlayChannel(-1, GUI_Theme::GetSound(GUI_Sounds::Shoot), 0);
//        m_Graphics.DrawObject(m_Heart, 10+20*i, 10);

    // Update screen
//    m_Graphics.Update();
}


//////////////////////////////////////////////////////////////////////////


void GUI_Field::SetID( int _ID )
{
    m_ID = _ID;
}


//////////////////////////////////////////////////////////////////////////


void GUI_Field::AddStateToRenderQueue( const ServerStatePacket& _packet )
{
    // Get packet creation time
    static size_t lastPacketTime = _packet.GetCreationTime();

    // If it is packet from the past - skip it
    if (_packet.GetCreationTime() >= lastPacketTime)
    {
        // Update time
        lastPacketTime = _packet.GetCreationTime();

        // Add packet to queue
        ScopeGuard guard(m_RenderQueue);
        m_RenderQueue.push_back( std::make_pair(_packet.GetCreationTime(), _packet.GetState() ) );
    }
}
